from typing import Any, Awaitable, Callable, Dict

from aiogram import BaseMiddleware
from aiogram.types import TelegramObject
from sqlalchemy.ext.asyncio import async_sessionmaker

from dc_shop.database.services import BotSettingsService
from dc_shop.database.services.delivery_zone import DeliveryZoneService
from dc_shop.database.services.discount import DiscountService
from dc_shop.database.services.order import OrderService
from dc_shop.database.services.payment_method import PaymentMethodService
from dc_shop.database.services.product import ProductService
from dc_shop.database.services.promocode import PromocodeService
from dc_shop.database.services.shop import ShopService
from dc_shop.database.services.statistics import StatsService
from dc_shop.database.services.ticket import TicketService
from dc_shop.database.services.user import UserService
from dc_shop.database.uow.uow import build_uow


class DBSessionMiddleware(BaseMiddleware):
    def __init__(self, sessionmaker: async_sessionmaker) -> None:
        self.sessionmaker = sessionmaker

    async def __call__(
        self,
        handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: Dict[str, Any],
    ) -> Any:
        async with self.sessionmaker() as session:
            uow = build_uow(session)
            data["uow"] = uow
            data["user_service"] = UserService(uow)
            data["order_service"] = OrderService(uow)
            data["delivery_zone_service"] = DeliveryZoneService(uow)
            data["product_service"] = ProductService(uow)
            data["payment_method_service"] = PaymentMethodService(uow)
            data["shop_service"] = ShopService(uow)
            data["ticket_service"] = TicketService(uow)
            data["stats_service"] = StatsService(uow)
            data["discount_service"] = DiscountService(uow)
            data["promocode_service"] = PromocodeService(uow)
            data["bot_settings_service"] = BotSettingsService(uow)

            result = await handler(event, data)
            return result
